/*
 *  Created on: May 17, 2009
 *      Author: Ramesh Rajaby
 *
 *  While I was writing this, I noticed that there is repeated and redundant code.
 *  Since this is "quite challenging" code, I didn't have the guts to adjust that.
 *  It's not a problem, though.
 */


#ifndef _AST_DECOMPOSER_
#define _AST_DECOMPOSER_


#include "../AST/AST.h"
#include "BytecodeStructures.h"


using namespace SymphonyAST;


namespace SymphonyBC {


class ASTDecomposer {
private:
	map<string, int> opcodesMap;

	vector<char> dataArea;

	void assignConstant(int& dest, string source);
	int countOccurrences(Variable* var, Expression* exp, int level);

public:
	ASTDecomposer() {
		ifstream opcodes("opcodes");

		if (!opcodes)
			throw "opcodes file not found";

		string instr; int opcode;
		while (opcodes >> instr >> opcode)
			opcodesMap[instr] = opcode;
	}

	ClassBC* decomposeClass(Class* clazz);
	MethodBC* decomposeMethod(Method* method);
	void decomposeScope(Scope* scope, vector<InstructionBC*>& instructions);
	void decomposeExpression(Expression*& exp, vector<InstructionBC*>& instructions);
	void decomposeAssignment(const string& op, Expression* exp1, Expression* exp2, vector<InstructionBC*>& instructions);
	void decomposeArithmeticOp(Expression* exp, vector<InstructionBC*>& instructions);
	void decomposeUnaryOp(Expression* exp, vector<InstructionBC*>& instructions);
	void decomposeTernaryArithmeticOp(const string& op, Expression* exp, Expression* op1, Expression* op2, vector<InstructionBC*>& instructions);
	void decomposeRelationalOp(Expression* exp, vector<InstructionBC*>& instructions);
	void decomposeLogicalOp(Expression* exp, vector<InstructionBC*>& instructions);
	void decomposeWhile(While* _while, vector<InstructionBC*>& instructions);
	void decomposeIf(If* _if, vector<InstructionBC*>& instructions);
	void decomposeFor(For* _for, vector<InstructionBC*>& instructions);
	void decomposeCall(Expression* call, vector<InstructionBC*>& instructions);
	void decomposeReturn(Return* r3turn, vector<InstructionBC*>& instructions);
	void decomposePrint(Print* print, vector<InstructionBC*>& instructions);
	void decomposeScan(Scan* scan, vector<InstructionBC*>& instructions);

	void optimizeClass(Class* clazz);
	void optimizeMethod(Method* method);
	void optimizeScope(Scope* scope);
	void optimizeExpression(Expression* exp);
};


}


using namespace SymphonyBC;


inline ClassBC* ASTDecomposer::decomposeClass(Class* clazz) {
	optimizeClass(clazz);

	ClassBC* classBC = new ClassBC;
	classBC->setName(clazz->getName());

	vector<string> methodsIds = clazz->getMethodsIds();
	for (int i = 0; i < (int)methodsIds.size(); i++) {
		string methodName = methodsIds[i];
#ifdef DEBUG
		cout << methodName << endl;
#endif
		MethodBC* methodBC = decomposeMethod(clazz->getMethod(methodName));
		classBC->addMethod(methodName, methodBC);
#ifdef DEBUG
		cout << "exit" << endl;
		cout << endl;
#endif
	}

	string temp;
	for (int i = 0; i < (int)dataArea.size(); i++) {
		temp += dataArea[i];
	}
	classBC->setDataArea(temp);

	return classBC;
}


inline MethodBC* ASTDecomposer::decomposeMethod(Method* method) {
	MethodBC* methodBC = new MethodBC;
	methodBC->setName(method->getName());
	methodBC->setReturnType(method->getReturnTypeId());

	vector<InstructionBC*> instructions;
	decomposeScope(method, instructions);
	InstructionBC* exitMethod = new InstructionBC;
	exitMethod->opcode = opcodesMap["exit_method"];
	instructions.push_back(exitMethod);

	BOOST_FOREACH(InstructionBC* instruction, instructions) {
		methodBC->addInstruction(instruction);
	}

	return methodBC;
}


inline void ASTDecomposer::decomposeScope(Scope* scope, vector<InstructionBC*>& instructions) {
	BOOST_FOREACH(Scope* child, scope->getChildren()) {
		string nodeId = child->getId();

		if (nodeId == "exp") {
			Expression* expChild = (Expression*)child;
			decomposeExpression(expChild, instructions);
		} else if (nodeId == "while") {
			While* whileChild = (While*)child;
			decomposeWhile(whileChild, instructions);
		} else if (nodeId == "if") {
			If* ifChild = (If*)child;
			decomposeIf(ifChild, instructions);
		} else if (nodeId == "for") {
			For* forChild = (For*)child;
			decomposeFor(forChild, instructions);
		} else if (nodeId == "return") {
			Return* returnChild = (Return*)child;
			decomposeReturn(returnChild, instructions);
		} else if (nodeId == "print") {
			Print* printChild = (Print*)child;
			decomposePrint(printChild, instructions);
		} else if (nodeId == "scan") {
			Scan* scanChild = (Scan*)child;
			decomposeScan(scanChild, instructions);
		} else if (nodeId == "decl") {
			Declaration* declarationChild = (Declaration*)child;
			string name = declarationChild->getName();
			scope->addVariable(name, declarationChild->getVar());
		}
	}
}


inline void ASTDecomposer::decomposeExpression(Expression*& exp, vector<InstructionBC*>& instructions) {
	if (exp->op != "&&" && exp->op != "||" && exp->op != "call") {
		vector<Scope*> children = exp->getChildren();
		for (int i = 0; i < (int)children.size(); i++) {
			Expression* expChild = (Expression*)children[i];

			decomposeExpression(expChild, instructions);

			//solves the problem of conflicting temp variables
			if (startsWith(expChild->lvalue, "$")) {
				exp->incOffset(expChild->getOffset());
			}
		}
	}

	string op = exp->op;
	if (isAssignmentOp(op)) {
		decomposeAssignment(op, exp, (Expression*) exp->getChild(0), instructions);
	} else if (op == "+" || op == "*") {
		decomposeArithmeticOp(exp, instructions);
	} else if (op == "<" || op == "<=" || op == ">" || op == ">=" || op == "==" || op == "!=") {
		decomposeRelationalOp(exp, instructions);
	} else if (op == "&&" || op == "||") {
		decomposeLogicalOp(exp, instructions);
	} else if (op == "-x" || op == "++x" || op == "--x") {
		decomposeUnaryOp(exp, instructions);
	} else if (op == "call") {
		decomposeCall(exp, instructions);
	}
}

//TODO: what the hell is op??
inline void ASTDecomposer::decomposeAssignment(const string& op, Expression* exp1, Expression* exp2, vector<InstructionBC*>& instructions) {
	Variable* v1 = exp1->getLVar();
	Variable* v2 = exp2->getLVar();

	string instr;
	if (op == "=") {
		instr = LowLevelInstructions::ASSIGN;
	} else if (op == "+=") {
		instr = LowLevelInstructions::ADD;
	} else if (op == "-=") {
		instr = LowLevelInstructions::SUB;
	} else if (op == "*=") {
		instr = LowLevelInstructions::MUL;
	} else if (op == "/=") {
		instr = LowLevelInstructions::DIV;
	} else if (op == "%=") {
		instr = LowLevelInstructions::MOD;
	}

	InstructionBC* instruction = new InstructionBC;

	string v2Type;
	if (v2 == NULL) {
		//it's a constant
		assignConstant(instruction->op2, exp2->lvalue);
		v2Type = "c" + PrimitiveTypes::KEYWORDS[getConstantType(exp2->lvalue)];

		//type inference
		if (v1 == NULL) {
			v1 = new Variable;
			v1->setType(getConstantType(exp2->lvalue));
			getFirstNonExpression(exp1)->addVariable(exp1->lvalue, v1);
		}
	} else {
		instruction->op2 = v2->getAddress();
		v2Type = PrimitiveTypes::KEYWORDS[v2->getType()];

		//type inference
		if (v1 == NULL) {
			v1 = new Variable;
			v1->setType(v2->getType());
			getFirstNonExpression(exp1)->addVariable(exp1->lvalue, v1);
		}
	}

	instruction->op1 = v1->getAddress();
	instr += "_" + PrimitiveTypes::KEYWORDS[v1->getType()] + "_" + v2Type;
	instruction->opcode = opcodesMap[instr];

	//TODO: this is quite dirty, should be externalized
	if (op != "=" || instruction->op1 != instruction->op2 || v2 == NULL) {
		instructions.push_back(instruction);
	}

#ifdef DEBUG
cout << instr + "  " + exp1->lvalue + ":" << instruction->op1 << "  " + exp2->lvalue + ":" << instruction->op2 << endl;
#endif
}


inline void ASTDecomposer::decomposeArithmeticOp(Expression* exp, vector<InstructionBC*>& instructions) {
	vector<Scope*> children = exp->getChildren();

	if (exp->lvalue == "") {
		Variable* v = new Variable;
		exp->lvalue = LanguageKeywords::RESERVED_VAR;
		//find supertype
		int supertype = PrimitiveTypes::CHAR;
		//Tries to determine what type the result variable should be.
		BOOST_FOREACH(Scope* child, children) {
			Variable* temp = child->getVariable(((Expression*)child)->lvalue);
			//If no temp, it means that it's a constant (we suppose the tree is error-free)
			supertype = getPrimitiveSupertype(supertype,
					temp == NULL ? getConstantType(((Expression*)child)->lvalue) : temp->getType());
		}
		v->setType(supertype);
		exp->addVariable(LanguageKeywords::RESERVED_VAR, v);
	} else if (exp->getLVar() == NULL) {
		Variable* vres = new Variable;

		//TODO: this should be extracted, since it's duplicated
		int supertype = PrimitiveTypes::CHAR;
		//Tries to determine what type the result variable should be.
		BOOST_FOREACH(Scope* child, exp->getChildren()) {
			Variable* temp = child->getVariable(((Expression*)child)->lvalue);
			//If no temp, it means that it's a constant (we suppose the tree is error-free)
			supertype = getPrimitiveSupertype(supertype,
					temp == NULL ? getConstantType(((Expression*)child)->lvalue) : temp->getType());
		}
		vres->setType(supertype);
		getFirstNonExpression(exp)->addVariable(exp->lvalue, vres);
	}

	Expression* child1 = (Expression*)children[0],
			  * child2 = (Expression*)children[1];

	decomposeTernaryArithmeticOp(child1->parentOpFlag, exp, child1, child2, instructions);
	for (int i = 2; i < (int)children.size(); i++) {
		string temp = i > 0 ? ((Expression*)children[i-1])->parentOpFlag + "=" : "=";
		decomposeAssignment(temp, exp, (Expression*)children[i], instructions);
	}
}


inline void ASTDecomposer::decomposeUnaryOp(Expression* exp, vector<InstructionBC*>& instructions) {
	Expression* child = (Expression*)exp->getChild(0);

	if (exp->lvalue == "") {
		Variable* v = new Variable;
		exp->lvalue = LanguageKeywords::RESERVED_VAR;
		v->setType(child->getLVar()->getType());
		exp->addVariable(LanguageKeywords::RESERVED_VAR, v);
	}

	string op = exp->op;
	string instr;
	InstructionBC* unInstr = new InstructionBC;
	if (op == "-x") {
		instr = "neg_" + PrimitiveTypes::KEYWORDS[exp->getLVar()->getType()] + "_" +
					PrimitiveTypes::KEYWORDS[child->getLVar()->getType()];
		unInstr->op2 = child->getLVar()->getAddress();
	} else if (exp->op == "++x") {
		instr = "add_" + PrimitiveTypes::KEYWORDS[exp->getLVar()->getType()] + "_c" +
							PrimitiveTypes::KEYWORDS[child->getLVar()->getType()];
		unInstr->op2 = 1;
	} else if (exp->op == "--x") {
		instr = "sub_" + PrimitiveTypes::KEYWORDS[exp->getLVar()->getType()] + "_c" +
					PrimitiveTypes::KEYWORDS[child->getLVar()->getType()];
		unInstr->op2 = 1;
	}
	unInstr->opcode = opcodesMap[instr];
	unInstr->op1 = exp->getLVar()->getAddress();

#ifdef DEBUG
	cout << instr << "  " << exp->lvalue << ":" << unInstr->op1 << "  " << child->lvalue << ":" << unInstr->op2 << endl;
#endif

	instructions.push_back(unInstr);
}


inline void ASTDecomposer::decomposeTernaryArithmeticOp(const string& op, Expression* res, Expression* op1, Expression* op2, vector<InstructionBC*>& instructions) {
	Variable* vres = res->getLVar();
	Variable* vop1 = op1->getLVar();
	Variable* vop2 = op2->getLVar();

	string instr;
	if (op == "+") {
		instr = LowLevelInstructions::ADD3;
		if (vop1 == NULL) {
			swap(vop1, vop2);
			swap(op1, op2);
		}
	} else if (op == "-") {
		instr = LowLevelInstructions::SUB3;
	} else if (op == "*") {
		instr = LowLevelInstructions::MUL3;
		if (vop1 == NULL) {
			swap(vop1, vop2);
			swap(op1, op2);
		}
	} else if (op == "/") {
		instr = LowLevelInstructions::DIV3;
	} else if (op == "%") {
		instr = LowLevelInstructions::MOD3;
	}

	instr += "_" + PrimitiveTypes::KEYWORDS[vres->getType()];

	InstructionBC* instruction = new InstructionBC;
	instruction->op1 = vres->getAddress();

	string v1Type, v2Type;
	if (vop1 == NULL) {
		v1Type = "c" + PrimitiveTypes::KEYWORDS[getConstantType(op1->lvalue)];
		v2Type = PrimitiveTypes::KEYWORDS[vop2->getType()];
		assignConstant(instruction->op2, op1->lvalue);
		instruction->op3 = vop2->getAddress();
	} else if (vop2 == NULL) {
		v1Type = PrimitiveTypes::KEYWORDS[vop1->getType()];
		v2Type = "c" + PrimitiveTypes::KEYWORDS[getConstantType(op2->lvalue)];
		instruction->op2 = vop1->getAddress();
		assignConstant(instruction->op3, op2->lvalue);
	} else {
		v1Type = PrimitiveTypes::KEYWORDS[vop1->getType()];
		v2Type = PrimitiveTypes::KEYWORDS[vop2->getType()];
		instruction->op2 = vop1->getAddress();
		instruction->op3 = vop2->getAddress();
	}

	instr += "_" + v1Type + "_" + v2Type;
	instruction->opcode = opcodesMap[instr];

	instructions.push_back(instruction);

#ifdef DEBUG
cout << instr + "  " + res->lvalue + ":" << instruction->op1 << "  " + op1->lvalue + ":" << instruction->op2;
cout << "  " << op2->lvalue << ":" << instruction->op3 << endl;
#endif
}


inline void ASTDecomposer::decomposeRelationalOp(Expression* exp, vector<InstructionBC*>& instructions) {
	if (exp->lvalue == "") {
		Variable* v = new Variable;
		exp->lvalue = LanguageKeywords::RESERVED_VAR;
		v->setType(PrimitiveTypes::CHAR);
		exp->addVariable(LanguageKeywords::RESERVED_VAR, v);
	}

	Expression* op1 = (Expression*)exp->getChildren()[0],
			  * op2 = (Expression*)exp->getChildren()[1];
	Variable* vres = exp->getLVar(),
			* vop1 = op1->getLVar(),
			* vop2 = op2->getLVar();

	string op = exp->op;
	string instr;
	if (op == "<") {
		instr = LowLevelInstructions::LESS;
	} else if (op == "<=") {
		instr = LowLevelInstructions::LESSEQ;
	} else if (op == ">") {
		instr = LowLevelInstructions::LESS;
		swap(op1, op2);
		swap(vop1, vop2);
	} else if (op == ">=") {
		instr = LowLevelInstructions::LESSEQ;
		swap(op1, op2);
		swap(vop1, vop2);
	} else if (op == "==") {
		instr = LowLevelInstructions::EQUAL;
		if (vop1 == NULL) {
			swap(op1, op2);
			swap(vop1, vop2);
		}
	} else if (op == "!=") {
		instr = LowLevelInstructions::UNEQUAL;
		if (vop1 == NULL) {
			swap(op1, op2);
			swap(vop1, vop2);
		}
	}

	instr += "_" + PrimitiveTypes::KEYWORDS[vop1->getType()];
	instr += "_" + PrimitiveTypes::KEYWORDS[vop2->getType()];

	InstructionBC* instruction = new InstructionBC;
	instruction->opcode = opcodesMap[instr];
	instruction->op1 = vres->getAddress();

	string v1Type, v2Type;
	if (vop1 == NULL) {
		v1Type = "c" + PrimitiveTypes::KEYWORDS[getConstantType(op1->lvalue)];
		v2Type = PrimitiveTypes::KEYWORDS[vop2->getType()];
		assignConstant(instruction->op2, op1->lvalue);
		instruction->op3 = vop2->getAddress();
	} else if (vop2 == NULL) {
		v1Type = PrimitiveTypes::KEYWORDS[vop1->getType()];
		v2Type = "c" + PrimitiveTypes::KEYWORDS[getConstantType(op2->lvalue)];
		instruction->op2 = vop1->getAddress();
		assignConstant(instruction->op3, op2->lvalue);
	} else {
		v1Type = PrimitiveTypes::KEYWORDS[vop1->getType()];
		v2Type = PrimitiveTypes::KEYWORDS[vop2->getType()];
		instruction->op2 = vop1->getAddress();
		instruction->op3 = vop2->getAddress();
	}

	instructions.push_back(instruction);

#ifdef DEBUG
cout << instr + "  " + exp->lvalue + ":" << instruction->op1 << "  " + op1->lvalue + ":" << instruction->op2;
cout << "  " << op2->lvalue << ":" << instruction->op3 << endl;
#endif
}


inline void ASTDecomposer::decomposeLogicalOp(Expression* exp, vector<InstructionBC*>& instructions) {
	vector<InstructionBC*> jumps;

	BOOST_FOREACH(Scope* scope, exp->getChildren()) {
		Expression* expChild = (Expression*)scope;

		Expression* lvalue = (Expression*)(expChild->getChild(0)),
				  * rvalue = (Expression*)(expChild->getChild(1));
		decomposeExpression(lvalue, instructions);
		expChild->incOffset(lvalue->getOffset());
		decomposeExpression(rvalue, instructions);

		string instr;
		if (exp->op == "&&") {
			if (expChild->op == "<") {
				instr = LowLevelInstructions::JLESSEQ;
				swap(lvalue, rvalue);
			} else if (expChild->op == "<=") {
				instr = LowLevelInstructions::JLESS;
				swap(lvalue, rvalue);
			} else if (expChild->op == ">") {
				instr = LowLevelInstructions::JLESSEQ;
			} else if (expChild->op == ">=") {
				instr = LowLevelInstructions::JLESS;
			} else if (expChild->op == "==") {
				instr = LowLevelInstructions::JUNEQUAL;
				if (lvalue->getLVar() == NULL) {
					swap(lvalue, rvalue);
				}
			} else if (expChild->op == "!=") {
				instr = LowLevelInstructions::JEQUAL;
				if (lvalue->getLVar() == NULL) {
					swap(lvalue, rvalue);
				}
			}
		} else if (exp->op == "||") {
			if (expChild->op == "<") {
				instr = LowLevelInstructions::JLESS;
			} else if (expChild->op == "<=") {
				instr = LowLevelInstructions::JLESSEQ;
			} else if (expChild->op == ">") {
				instr = LowLevelInstructions::JLESS;
				swap(lvalue, rvalue);
			} else if (expChild->op == ">=") {
				instr = LowLevelInstructions::JLESSEQ;
				swap(lvalue, rvalue);
			} else if (expChild->op == "==") {
				instr = LowLevelInstructions::JEQUAL;
				if (lvalue->getLVar() == NULL) {
					swap(lvalue, rvalue);
				}
			} else if (expChild->op == "!=") {
				instr = LowLevelInstructions::JUNEQUAL;
				if (lvalue->getLVar() == NULL) {
					swap(lvalue, rvalue);
				}
			}
		}

		Variable* lvar = lvalue->getLVar(),
				* rvar = rvalue->getLVar();

		InstructionBC* instruction = new InstructionBC;

		//since we are accessing the expression tree in a "too direct way",
		//lvalue and/or rvalue may be constants. Let's deal with that.
		if (lvar == NULL) {
			assignConstant(instruction->op2, lvalue->lvalue);
			instruction->op3 = rvar->getAddress();
			instr += "_c" + PrimitiveTypes::KEYWORDS[getConstantType(lvalue->lvalue)] +
					 "_" + PrimitiveTypes::KEYWORDS[rvar->getType()];
		} else if (rvar == NULL) {
			instruction->op2 = lvar->getAddress();
			assignConstant(instruction->op3,rvalue->lvalue);
			instr += "_c" + PrimitiveTypes::KEYWORDS[lvar->getType()] +
					 "_" + PrimitiveTypes::KEYWORDS[getConstantType(rvalue->lvalue)];
		} else {
			instruction->op2 = lvar->getAddress();
			instruction->op3 = rvar->getAddress();
			instr += "_c" + PrimitiveTypes::KEYWORDS[lvar->getType()] +
					 "_" + PrimitiveTypes::KEYWORDS[rvar->getType()];
		}

		instruction->opcode = opcodesMap[instr];

		instructions.push_back(instruction);
		jumps.push_back(instruction);

#ifdef DEBUG
cout << instr << "  " << lvalue->lvalue + ":" << instruction->op2;
cout << "  " << rvalue->lvalue << ":" << instruction->op3 << endl;
#endif
	}

	if (exp->lvalue == "") {
		Variable* v = new Variable;
		exp->lvalue = LanguageKeywords::RESERVED_VAR;
		v->setType(PrimitiveTypes::CHAR);
		exp->addVariable(LanguageKeywords::RESERVED_VAR, v);
	}

	//If everything is true (&&) or false (||), execution will reach this
	Variable* lvar = exp->getLVar();
	InstructionBC* assignInstruction = new InstructionBC;

	string instr;
	assignInstruction->op1 = lvar->getAddress();
	if (exp->op == "&&") {
		assignInstruction->opcode = opcodesMap[instr = "ass1_and_jrel2"];
	} else if (exp->op == "||") {
		assignInstruction->opcode = opcodesMap[instr = "ass0_and_jrel2"];
	}

	instructions.push_back(assignInstruction);

#ifdef DEBUG
cout << instr + "  " + exp->lvalue + ":" << assignInstruction->op1 << endl;
#endif

	//Otherwise, execution will get here
	int false_label = instructions.size();
	assignInstruction = new InstructionBC;
	assignInstruction->opcode = opcodesMap[instr = "ass_int_cint"];
	assignInstruction->op1 = lvar->getAddress();
	if (exp->op == "&&") {
		assignInstruction->op2 = 0;
	} else if (exp->op == "||") {
		assignInstruction->op2 = 1 ;
	}

	instructions.push_back(assignInstruction);

#ifdef DEBUG
cout << instr + "  " + exp->lvalue + ":" << assignInstruction->op1 << "  " << "0:0" << endl;
#endif

	BOOST_FOREACH(InstructionBC* instruction, jumps) {
		instruction->op1 = false_label;
	}
}


inline void ASTDecomposer::decomposeWhile(While* _while, vector<InstructionBC*>& instructions) {
	Expression* exp = _while->expression;

	if (exp->op != "&&" && exp->op != "||") {
		Expression* newExp = new Expression();
		newExp->op = "&&";
		newExp->parent = exp->parent;
		newExp->addChild(exp);

		exp = newExp;
	}

	vector<InstructionBC*> jumps;
	int repeat_label = instructions.size();

	BOOST_FOREACH(Scope* scope, exp->getChildren()) {
		Expression* expChild = (Expression*)scope;

		Expression* lvalue = (Expression*)(expChild->getChild(0)),
				  * rvalue = (Expression*)(expChild->getChild(1));
		decomposeExpression(lvalue, instructions);
		//TODO: not size of lvalue->lvalue?
		expChild->incOffset(lvalue->getOffset());
		decomposeExpression(rvalue, instructions);

		string instr;
		if (exp->op == "&&") {
			if (expChild->op == "<") {
				instr = LowLevelInstructions::JLESSEQ;
				swap(lvalue, rvalue);
			} else if (expChild->op == "<=") {
				instr = LowLevelInstructions::JLESS;
				swap(lvalue, rvalue);
			} else if (expChild->op == ">") {
				instr = LowLevelInstructions::JLESSEQ;
			} else if (expChild->op == ">=") {
				instr = LowLevelInstructions::JLESS;
			} else if (expChild->op == "==") {
				instr = LowLevelInstructions::JUNEQUAL;
				if (lvalue->getLVar() == NULL) {
					swap(lvalue, rvalue);
				}
			} else if (expChild->op == "!=") {
				instr = LowLevelInstructions::JEQUAL;
				if (lvalue->getLVar() == NULL) {
					swap(lvalue, rvalue);
				}
			}
		} else if (exp->op == "||") {
			if (expChild->op == "<") {
				instr = LowLevelInstructions::JLESS;
			} else if (expChild->op == "<=") {
				instr = LowLevelInstructions::JLESSEQ;
			} else if (expChild->op == ">") {
				instr = LowLevelInstructions::JLESS;
				swap(lvalue, rvalue);
			} else if (expChild->op == ">=") {
				instr = LowLevelInstructions::JLESSEQ;
				swap(lvalue, rvalue);
			} else if (expChild->op == "==") {
				instr = LowLevelInstructions::JEQUAL;
				if (lvalue->getLVar() == NULL) {
					swap(lvalue, rvalue);
				}
			} else if (expChild->op == "!=") {
				instr = LowLevelInstructions::JUNEQUAL;
				if (lvalue->getLVar() == NULL) {
					swap(lvalue, rvalue);
				}
			}
		}

		Variable* lvar = lvalue->getLVar(),
				* rvar = rvalue->getLVar();

		InstructionBC* instruction = new InstructionBC;

		//since we are accessing the expression tree in a "too direct way",
		//lvalue and/or rvalue may be constants. Let's deal with that.
		if (lvar == NULL) {
			assignConstant(instruction->op2, lvalue->lvalue);
			instruction->op3 = rvar->getAddress();
			instr += "_c" + PrimitiveTypes::KEYWORDS[getConstantType(lvalue->lvalue)] +
					 "_" + PrimitiveTypes::KEYWORDS[rvar->getType()];
		} else if (rvar == NULL) {
			instruction->op2 = lvar->getAddress();
			assignConstant(instruction->op3, rvalue->lvalue);
			instr += "_" + PrimitiveTypes::KEYWORDS[lvar->getType()] +
					 "_c" + PrimitiveTypes::KEYWORDS[getConstantType(rvalue->lvalue)];
		} else {
			instruction->op2 = lvar->getAddress();
			instruction->op3 = rvar->getAddress();
			instr += "_" + PrimitiveTypes::KEYWORDS[lvar->getType()] +
					 "_" + PrimitiveTypes::KEYWORDS[rvar->getType()];
		}

		instruction->opcode = opcodesMap[instr];

		instructions.push_back(instruction);
		jumps.push_back(instruction);

#ifdef DEBUG
cout << instr << "  " << lvalue->lvalue + ":" << instruction->op2;
cout << "  " << rvalue->lvalue << ":" << instruction->op3 << endl;
#endif
	}

	if (exp->op == "||") {
		int do_label = instructions.size();
		BOOST_FOREACH(InstructionBC* instruction, jumps) {
			instruction->op1 = do_label;
		}

		//the logic is reversed compared to &&: if I get here, than the expression is false, so I have to jump away
		jumps.clear();

		InstructionBC* jInstruction = new InstructionBC;
		jInstruction->opcode = opcodesMap[LowLevelInstructions::JABS];
		instructions.push_back(jInstruction);
		jumps.push_back(jInstruction);
#ifdef DEBUG
cout << LowLevelInstructions::JABS << endl;
#endif
	}

	decomposeScope(_while, instructions);

	//Jump back
	InstructionBC* jInstruction = new InstructionBC;
	jInstruction->opcode = opcodesMap[LowLevelInstructions::JABS];
	jInstruction->op1 = repeat_label;

	instructions.push_back(jInstruction);

#ifdef DEBUG
cout << LowLevelInstructions::JABS << " " << repeat_label << endl;
#endif

	//If expression is false, execution will get here
	int skip_label = instructions.size();
	if (exp->op == "&&") {
		BOOST_FOREACH(InstructionBC* instruction, jumps) {
			instruction->op1 = skip_label;
		}
	} else if (exp->op == "||") {
		jumps[0]->op1 = skip_label;
	}
}


inline void ASTDecomposer::decomposeIf(If* _if, vector<InstructionBC*>& instructions) {
	vector<InstructionBC*> jumpOut;

	BOOST_FOREACH(Scope* child, _if->getChildren()) {
		Branch* branch = (Branch*)child;
		Expression* exp = branch->expression;

		if (exp == NULL) { //this should mean it's an else
			decomposeScope(branch, instructions);
			continue;
		}

		if (exp->op != "&&" && exp->op != "||") {
			Expression* newExp = new Expression();
			newExp->op = "&&";
			newExp->parent = exp->parent;
			newExp->addChild(exp);

			exp = newExp;
		}

		vector<InstructionBC*> jumps;

		BOOST_FOREACH(Scope* scope, exp->getChildren()) {
			Expression* expChild = (Expression*)scope;

			Expression* lvalue = (Expression*)(expChild->getChild(0)),
					  * rvalue = (Expression*)(expChild->getChild(1));
			decomposeExpression(lvalue, instructions);
			expChild->incOffset(lvalue->getOffset());
			decomposeExpression(rvalue, instructions);

			string instr;
			if (exp->op == "&&") {
				if (expChild->op == "<") {
					instr = LowLevelInstructions::JLESSEQ;
					swap(lvalue, rvalue);
				} else if (expChild->op == "<=") {
					instr = LowLevelInstructions::JLESS;
					swap(lvalue, rvalue);
				} else if (expChild->op == ">") {
					instr = LowLevelInstructions::JLESSEQ;
				} else if (expChild->op == ">=") {
					instr = LowLevelInstructions::JLESS;
				} else if (expChild->op == "==") {
					instr = LowLevelInstructions::JUNEQUAL;
					if (lvalue->getLVar() == NULL) {
						swap(lvalue, rvalue);
					}
				} else if (expChild->op == "!=") {
					instr = LowLevelInstructions::JEQUAL;
					if (lvalue->getLVar() == NULL) {
						swap(lvalue, rvalue);
					}
				}
			} else if (exp->op == "||") {
				if (expChild->op == "<") {
					instr = LowLevelInstructions::JLESS;
				} else if (expChild->op == "<=") {
					instr = LowLevelInstructions::JLESSEQ;
				} else if (expChild->op == ">") {
					instr = LowLevelInstructions::JLESS;
					swap(lvalue, rvalue);
				} else if (expChild->op == ">=") {
					instr = LowLevelInstructions::JLESSEQ;
					swap(lvalue, rvalue);
				} else if (expChild->op == "==") {
					instr = LowLevelInstructions::JEQUAL;
					if (lvalue->getLVar() == NULL) {
						swap(lvalue, rvalue);
					}
				} else if (expChild->op == "!=") {
					instr = LowLevelInstructions::JUNEQUAL;
					if (lvalue->getLVar() == NULL) {
						swap(lvalue, rvalue);
					}
				}
			}

			Variable* lvar = lvalue->getLVar(),
					* rvar = rvalue->getLVar();

			InstructionBC* instruction = new InstructionBC;

			//since we are accessing the expression tree in a "too direct way",
			//lvalue and/or rvalue may be constants. Let's deal with that.
			if (lvar == NULL) {
				assignConstant(instruction->op2, lvalue->lvalue);
				instruction->op3 = rvar->getAddress();
				instr += "_c" + PrimitiveTypes::KEYWORDS[getConstantType(lvalue->lvalue)] +
						 "_" + PrimitiveTypes::KEYWORDS[rvar->getType()];
			} else if (rvar == NULL) {
				instruction->op2 = lvar->getAddress();
				assignConstant(instruction->op3,rvalue->lvalue);
				instr += "_" + PrimitiveTypes::KEYWORDS[lvar->getType()] +
						 "_c" + PrimitiveTypes::KEYWORDS[getConstantType(rvalue->lvalue)];
			} else {
				instruction->op2 = lvar->getAddress();
				instruction->op3 = rvar->getAddress();
				instr += "_c" + PrimitiveTypes::KEYWORDS[lvar->getType()] +
						 "_" + PrimitiveTypes::KEYWORDS[rvar->getType()];
			}

			instruction->opcode = opcodesMap[instr];

			instructions.push_back(instruction);
			jumps.push_back(instruction);

#ifdef DEBUG
cout << instr << "  " << lvalue->lvalue + ":" << instruction->op2;
cout << "  " << rvalue->lvalue << ":" << instruction->op3 << endl;
#endif
		}

		if (exp->op == "||") {
			int do_label = instructions.size();
			BOOST_FOREACH(InstructionBC* instruction, jumps) {
				instruction->op1 = do_label;
			}

			//the logic is reversed compared to &&: if I get here, than the expression is false, so I have to jump away
			jumps.clear();

			InstructionBC* jInstruction = new InstructionBC;
			jInstruction->opcode = opcodesMap[LowLevelInstructions::JABS];
			instructions.push_back(jInstruction);
			jumps.push_back(jInstruction);
#ifdef DEBUG
cout << LowLevelInstructions::JABS << endl;
#endif
		}

		decomposeScope(branch, instructions);

		//Jump out
		if (_if->getChildren().size() > 1) {
			InstructionBC* jInstruction = new InstructionBC;
			jInstruction->opcode = opcodesMap[LowLevelInstructions::JABS];

			jumpOut.push_back(jInstruction);
			instructions.push_back(jInstruction);

#ifdef DEBUG
cout << LowLevelInstructions::JABS << " out" << endl;
#endif
		}

		//If expression is false, execution will get here
		int skip_label = instructions.size();
		if (exp->op == "&&") {
			BOOST_FOREACH(InstructionBC* instruction, jumps) {
				instruction->op1 = skip_label;
			}
		} else if (exp->op == "||") {
			jumps[0]->op1 = skip_label;
		}
	}

	int outLabel = instructions.size();
	BOOST_FOREACH(InstructionBC* instruction, jumpOut) {
		instruction->op1 = outLabel;
	}
}


inline void ASTDecomposer::decomposeFor(For* _for, vector<InstructionBC*>& instructions) {
	decomposeExpression(_for->start, instructions);
	InstructionBC* initVarInstr = new InstructionBC;
	instructions.push_back(initVarInstr);
	decomposeExpression(_for->end, instructions);

	Variable* startVar = _for->start->getVariable(_for->start->lvalue),
			* endVar = _for->end->getVariable(_for->end->lvalue),
			* forVar = new Variable,
			* stepVar = _for->getVariable(_for->step->lvalue);
	int varType = getPrimitiveSupertype(
				startVar == NULL ? getConstantType(_for->start->lvalue) : startVar->getType(),
				endVar == NULL ? getConstantType(_for->end->lvalue) : endVar->getType()
			);
	forVar->setType(varType);
	_for->addVariable(_for->var, forVar);

	//Compose the first assignment instruction
	string instr = "ass_" + PrimitiveTypes::KEYWORDS[varType] + "_" +
			(startVar == NULL ? "c" + PrimitiveTypes::KEYWORDS[getConstantType(_for->start->lvalue)] :
							   PrimitiveTypes::KEYWORDS[startVar->getType()]);

	initVarInstr->opcode = opcodesMap[instr];
	initVarInstr->op1 = forVar->getAddress();
	if (startVar != NULL) {
		initVarInstr->op2 = startVar->getAddress();
	} else {
		assignConstant(initVarInstr->op2, _for->start->lvalue);
	}

#ifdef DEBUG
cout << instr << " " << endl;
#endif

	int jumpBackLabel = instructions.size();

	//Compose the jump instruction
	InstructionBC* jumpInstr = new InstructionBC;

	instr = "jlesseq_";
	if (_for->up) {
		instr += (endVar == NULL ? "c" + PrimitiveTypes::KEYWORDS[getConstantType(_for->end->lvalue)] :
										 PrimitiveTypes::KEYWORDS[endVar->getType()]);
		instr += "_" + PrimitiveTypes::KEYWORDS[forVar->getType()];

		if (endVar != NULL) {
			jumpInstr->op2 = endVar->getAddress();
		} else {
			assignConstant(jumpInstr->op2, _for->end->lvalue);
		}
		jumpInstr->op3 = forVar->getAddress();
	} else {
		instr += PrimitiveTypes::KEYWORDS[forVar->getType()] + "_";
		instr += (endVar == NULL ? "c" + PrimitiveTypes::KEYWORDS[getConstantType(_for->end->lvalue)] :
												 PrimitiveTypes::KEYWORDS[endVar->getType()]);

		jumpInstr->op2 = forVar->getAddress();
		if (endVar != NULL) {
			jumpInstr->op3 = endVar->getAddress();
		} else {
			assignConstant(jumpInstr->op3, _for->end->lvalue);
		}
	}

	jumpInstr->opcode = opcodesMap[instr];

	instructions.push_back(jumpInstr);

#ifdef DEBUG
cout << instr << " " << endl;
#endif

	//Decompose the body
	decomposeScope(_for, instructions);

	decomposeExpression(_for->step, instructions);

	//Inc/dec var and jump back
	InstructionBC* addAndJump = new InstructionBC;
	instr = _for->up ? "add" : "sub";
	instr += "_jump_" + PrimitiveTypes::KEYWORDS[forVar->getType()] + "_";

	addAndJump->op1 = jumpBackLabel;
	addAndJump->op2 = forVar->getAddress();
	if (stepVar != NULL) {
		addAndJump->op3 = stepVar->getAddress();
		instr += PrimitiveTypes::KEYWORDS[stepVar->getType()];
	} else {
		assignConstant(addAndJump->op3, _for->step->lvalue);
		instr += "c" + PrimitiveTypes::KEYWORDS[getConstantType(_for->step->lvalue)];
	}

	addAndJump->opcode = opcodesMap[instr];

	instructions.push_back(addAndJump);

	//Set the jump target
	jumpInstr->op1 = instructions.size();

#ifdef DEBUG
cout << instr << " " << endl;
#endif
}


inline void ASTDecomposer::decomposeCall(Expression* call, vector<InstructionBC*>& instructions) {
	vector<Scope*> children = call->getChildren();

	Class* clazz = getContainingClass(call);
	Method* methodToCall = clazz->getMethod(call->lvalue);
	vector<int> parametersTypes = methodToCall->getParametersTypes();

	InstructionBC* callInstruction = new InstructionBC;
	callInstruction->opcode = opcodesMap["call_this"];
	callInstruction->op1 = 0;
	callInstruction->op2 = clazz->getMethodId(call->lvalue);
	callInstruction->op3 = call->getAbsOffset();

	Variable* f = new Variable;
	f->setType(methodToCall->getReturnTypeId());
	//add an index to disambigue the call in case of several calls to the same method
	call->lvalue = "$" + call->lvalue + lexical_cast<string, int>(callInstruction->op3);
	call->addVariable(call->lvalue, f);

	for (int i = 0; i < (int)children.size(); i++) {
		Expression* parChild = (Expression*) children[i];

		Variable* par = new Variable;
		par->setType(parametersTypes[i]);

		string parName = "$par" + lexical_cast<string, int>(i);

		//$param_id = rest of the expression
		call->addVariable(parName, par);
		parName.swap(call->lvalue);
		call->op = "=";
		call->clearChildren();
		call->addChild(parChild);
		optimizeExpression(call);
		decomposeExpression(call, instructions);
		parName.swap(call->lvalue);
	}
	call->setChildren(children);

	instructions.push_back(callInstruction);

#ifdef DEBUG
cout << "call " << call->lvalue << endl;
#endif
}


inline void ASTDecomposer::decomposeReturn(Return* r3turn, vector<InstructionBC*>& instructions) {
	decomposeExpression(r3turn->toReturn, instructions);

	InstructionBC* exitMethodInstruction = new InstructionBC;
	exitMethodInstruction->opcode = opcodesMap["exit_method"];
	instructions.push_back(exitMethodInstruction);

#ifdef DEBUG
cout << "exit" << endl;
#endif
}


inline void ASTDecomposer::decomposePrint(Print* print, vector<InstructionBC*>& instructions) {
	decomposeExpression(print->toPrint, instructions);

	Variable* var = print->toPrint->getLVar();

	InstructionBC* printInstruction = new InstructionBC;
	string instr;
	if (print->newLine) {
		instr = LowLevelInstructions::PRINTLN + "_" + (var == NULL ? "c" + PrimitiveTypes::KEYWORDS[getConstantType(print->toPrint->lvalue)] :
		                                    PrimitiveTypes::KEYWORDS[var->getType()]);
	} else {
		instr = LowLevelInstructions::PRINT + "_" + (var == NULL ? "c" + PrimitiveTypes::KEYWORDS[getConstantType(print->toPrint->lvalue)] :
				                            PrimitiveTypes::KEYWORDS[var->getType()]);
	}
	printInstruction->opcode = opcodesMap[instr];
	if (var == NULL) {
		assignConstant(printInstruction->op1, print->toPrint->lvalue);
	} else {
		printInstruction->op1 = print->toPrint->getLVar()->getAddress();
	}
	instructions.push_back(printInstruction);

#ifdef DEBUG
cout << instr << " " << print->toPrint->lvalue << ":" << printInstruction->op1 << endl;
#endif
}


inline void ASTDecomposer::decomposeScan(Scan* scan, vector<InstructionBC*>& instructions) {
	InstructionBC* scanInstruction = new InstructionBC;

	string instr = LowLevelInstructions::SCAN + "_" + PrimitiveTypes::KEYWORDS[scan->dest->getType()];
	scanInstruction->opcode = opcodesMap[instr];
	scanInstruction->op1 = scan->dest->getAddress();

	instructions.push_back(scanInstruction);

#ifdef DEBUG
cout << instr << " " << scan->dest->getAddress() << endl;
#endif
}


inline void ASTDecomposer::assignConstant(int& dest, string source) {
	trim(source);

	int ctype = getConstantType(source);

	if (!ctype) {
		throw "unexpected token: " + source;
	}

	if (ctype == PrimitiveTypes::INT) {
		dest = lexical_cast<int>(source);
	} else if (ctype == PrimitiveTypes::LONG) {
		if (source[source.length()-1] == 'l') {
			source.erase(source.length()-1);
		}

		long long temp = lexical_cast<long long>(source);
		dest = dataArea.size();
		char* tempBuffer = (char*)&temp;
		for (int i = 0; i < (int)sizeof(temp); i++) {
			dataArea.push_back(tempBuffer[i]);
		}
	} else if (ctype == PrimitiveTypes::FLOAT) {
		(*(float*)&(dest)) = lexical_cast<float>(source);
	} else if (ctype == PrimitiveTypes::DOUBLE) {
		if (source[source.length()-1] == 'd') {
			source.erase(source.length()-1);
		}

		double temp = lexical_cast<double>(source);
		dest = dataArea.size();
		char* tempBuffer = (char*)&temp;
		for (int i = 0; i < (int)sizeof(temp); i++) {
			dataArea.push_back(tempBuffer[i]);
		}
	} else if (ctype == PrimitiveTypes::CHAR) {
		(*(char*)&(dest)) = lexical_cast<char>(source.substr(1, source.length()-2));
	}
}


inline int ASTDecomposer::countOccurrences(Variable* var, Expression* exp, int level) {
	if (level < 0) {
		return 0;
	}

	int count = 0;
	BOOST_FOREACH(Scope* scope, exp->getChildren()) {
		Expression* expChild = (Expression*)scope;
		count += countOccurrences(var, expChild, level-1);
	}
	return count + (var == exp->getLVar() && exp->op != "call");
}

inline void ASTDecomposer::optimizeClass(Class* clazz) {
	BOOST_FOREACH(string methodName, clazz->getMethodsIds()) {
		Method* method = clazz->getMethod(methodName);
		optimizeMethod(method);
	}
}

inline void ASTDecomposer::optimizeMethod(Method* method) {
	optimizeScope(method);
}


inline void ASTDecomposer::optimizeScope(Scope* scope) {
	BOOST_FOREACH(Scope* child, scope->getChildren()) {
		string nodeId = child->getId();

		if (nodeId == "exp") {
			Expression* expChild = (Expression*)child;
			optimizeExpression(expChild);
		} else if (nodeId == "while") {
			While* whileChild = (While*)child;
			optimizeScope(whileChild);
		} else if (nodeId == "if") {
			If* ifChild = (If*)child;
			optimizeScope(ifChild);
		} else if (nodeId == "branch") {
			Branch* branchChild = (Branch*)child;
			optimizeScope(branchChild);
		} else if (nodeId == "for") {
			For* forChild = (For*)child;
			optimizeScope(forChild);
		} else if (nodeId == "return") {
			Return* returnChild = (Return*)child;
			optimizeExpression(returnChild->toReturn);
		} else if (nodeId == "print") {
			Print* printChild = (Print*)child;
			optimizeExpression(printChild->toPrint);
		}
	}
}

//TODO: var = f(exp) -> if var type is infered (so no previously declared), the code generated is faster.
//This is true only is exp is a decomponible expression. This is because var gets allocated after the
//result of exp, and so the return address of f is the address of var.
//This case should be extended.
inline void ASTDecomposer::optimizeExpression(Expression* exp) {
	if (exp == NULL) {
		return;
	}

	if (isAssignmentOp(exp->op)) {
		Expression* expChild = (Expression*)exp->getChild(0);

		//If there are occurrences under level 1 it doesn't matter, coz those get decomposed first.
		if ((expChild->op == "+" || expChild->op == "*") &&
				(exp->getLVar() == NULL || countOccurrences(exp->getLVar(), expChild, 1) == 0)) {
			exp->removeChild(0);
			BOOST_FOREACH(Scope* s, expChild->getChildren()) {
				exp->addChild(s);
			}
			exp->op = expChild->op;
		}
	}

	vector<Scope*> children = exp->getChildren();
	for (int i = 0; i < (int)children.size(); i++) {
		Expression* expChild = (Expression*)children[i];
		optimizeExpression(expChild);
	}
}

#endif
